﻿using mLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;




namespace eBus
{

    public class eBus_pack_struct
    {
        public static int MPUT1_PACK_DATA_MAX_LENGTH = 10240;
        public byte head1 = 0xFE;
        public byte head2 = 0xEF;
        public byte ptype = 0;
        public List<byte> data;
        public byte check;
        public eBus_pack_struct()
        {
            data = new List<byte>();
        }
        public eBus_pack_struct(eBus_pack_struct pack)
        {
            this.data = new List<byte>();
            this.head1 = pack.head1;
            this.head2 = pack.head2;
            this.ptype = pack.ptype;
            if (pack.data != null)
            {
                if (pack.data.Count > 0)
                {
                    this.data.AddRange(pack.data);
                }
            }
            this.check = pack.check;
        }

        public List<byte> pack_2_byteflow()
        {
            List<byte> btl = new List<byte>();
            btl.Add(this.head1);
            btl.Add(this.head2);
            btl.Add(this.ptype); 
            if (this.data == null || this.data.Count == 0)
            {
                btl.Add(0);
            }
            else if (this.data.Count < 128)
            {
                btl.Add((byte)(this.data.Count & 0x7F));
                btl.AddRange(this.data);
            }
            else if (this.data.Count < 16384)
            {
                btl.Add((byte)(this.data.Count | 0x80));
                btl.Add((byte)((this.data.Count >> 7) & 0x7F));
                btl.AddRange(this.data);
            }
            else
            {
                return null;
            }
            //求校验位
            byte sum = 0;
            for (int i = 2; i < btl.Count; i++)
            {
                sum += btl[i];
            }
            sum = (byte)(255 - sum);
            this.check = sum;
            btl.Add(this.check);
            return btl;
        }
    }

    public class eBus_core
    {
        /// <summary>
        /// 接收数据流队列
        /// </summary>
        private Queue<byte[]> receive_flow = new Queue<byte[]>();

        /// <summary>
        /// 数据流解析线程
        /// </summary>
        private Thread flow_2_pack_th;

        public delegate void eBus_TAKE_PACK(eBus_pack_struct pack);
        public delegate void eBus_pack_SendFlow(List<byte> btl);

        private event eBus_TAKE_PACK take_pack_=null;
        private event eBus_pack_SendFlow send_flow_ = null;

        private object synObj = new object();

        public eBus_core(eBus_pack_SendFlow send_flow)
        {
            this.send_flow_ = send_flow;
            flow_2_pack_th = new Thread(eBus_flow_2_pack_run);
            //允许后台线程
            flow_2_pack_th.IsBackground = true;
            flow_2_pack_th.Start();
        }
        public void eBus_flow_add(Byte bt)
        {
            lock (synObj)
            {
                receive_flow.Enqueue(new byte[]{bt});
            }

        }
        public void eBus_flow_add(byte[] bts)
        {
            lock (synObj)
            {
                receive_flow.Enqueue(bts);
            }
        }

        public void eBus_pack_send_flow(eBus_pack_struct pack)
        {
            if (send_flow_ != null) send_flow_(pack.pack_2_byteflow());
        }

        /// <summary>
        /// 订阅数据包
        /// </summary>
        /// <param name="event_back"></param>
        public void eBus_subscribe_pack(eBus_TAKE_PACK event_back)
        {
            take_pack_ += event_back;
        }
        /// <summary>
        /// 取消订阅数据包
        /// </summary>
        /// <param name="event_back"></param>
        public void eBus_unsubscribe_pack(eBus_TAKE_PACK event_back)
        {
            take_pack_ -= event_back;
        }

        private void eBus_flow_2_pack_run()
        {
            eBus_pack_struct pack=new eBus_pack_struct();
            byte[] bts;
            Byte step=0;
            Byte check = 0;
            int pack_data_len = 0;
            pack.head1 = 0xFE;
            pack.head2 = 0xEF;
            while (true)
            {
                if (receive_flow.Count<=0)
                {
                    Thread.Sleep(5);
                    continue;
                }
                //在多个线程都需要操作一个内存空间时，需要使用锁
                lock (synObj)
                {
                    bts = receive_flow.Dequeue();
                }
                foreach(byte data in bts)switch (step)
                {
                    case 0:
                        if (data == pack.head1)
                        {
                            step = 10;
                        }
                        else
                        {
                            Console.Write("不是头1" + "\r\n");
                        }
                        break;
                    case 10:
                        if (data == pack.head2)
                        {
                            step = 20;
                        }
                        else if (data != pack.head1)
                        {
                            step = 0;
                            Console.Write("不是头1也不是头2" + "\r\n");
                        }
                        break;
                    case 20:
                        pack.ptype = data;
                        check = data;
                        step = 30;
                        if (pack.data == null) pack.data = new List<byte>();
                        else pack.data.Clear();
                        pack_data_len = 0;
                        pack.data.Clear();
                        break;
                    //
                    case 30:
                        if ((data & 0x80) == 0x80)
                        {
                            step = 31;
                            pack_data_len = (data & 0x7F);
                        }
                        else
                        {
                            pack_data_len = data;
                            if (pack_data_len == 0) step = 50;
                            else step = 40;
                        }
                        check += data;
                        break;
                    case 31:
                        if ((data & 0x80) == 0x80)
                        {
                            step = 0;
                            Console.Write("数据长度错误\r\n");
                            break;
                        }
                        else
                        {
                            pack_data_len += (data << 7);
                            if (pack_data_len > eBus_pack_struct.MPUT1_PACK_DATA_MAX_LENGTH) step = 0;
                            if (pack_data_len == 0) step = 50;
                            else step = 40;
                        }
                        check += data;
                        break;
                    case 40:
                        pack.data.Add(data);
                        check += data;
                        if (pack.data.Count >= pack_data_len)
                        {
                            step = 50;
                        }
                        break;

                    case 50:
                        if (check + data == 255)
                        {
                            pack.check = data;
                            if (take_pack_ != null)
                            {
                                take_pack_(new eBus_pack_struct(pack));
                            }
                            Console.Write("解析完整包 - 包类型：" + pack.ptype.ToString() + "    包命令：" + Function.bytesToHexString(pack.data.ToArray(), 1, " ") + "\r\n");
                        }
                        else
                        {
                            step = 0;
                            Console.Write("校验错误" + "\r\n");
                        }
                        step = 0;
                        break;

                    default: step = 0; break;
                }
                //
            }
        }

    }

    public class eBus_boot
    {
        //进入BOOT模式
        public const Byte  EBUS_MCUBOOT_ENABLE                 =0x70;
        public const Byte  EBUS_MCUBOOT_ENABLE_ACK             =0x90;

        public const Byte EBUS_MCUBOOT_RESET = 0x72;
        public const Byte EBUS_MCUBOOT_RESET_ACK = 0x92;

        //请求升级
        public const Byte  EBUS_MCUBOOT_REQUEST                =0x74;
        public const Byte  EBUS_MCUBOOT_REQUEST_ACK            =0x94;
        //请求升级
        public const Byte  EBUS_MCUBOOT_DATA_WRITE             =0x78;
        public const Byte  EBUS_MCUBOOT_DATA_WRITE_ACK         =0x98;
        //请求升级
        public const Byte  EBUS_MCUBOOT_DATA_READ              =0x7A;
        public const Byte  EBUS_MCUBOOT_DATA_READ_ACK          =0x9A;

        public delegate void eBus_boot_PACKSEND_Event(eBus_pack_struct pack);
        public delegate void eBus_boot_DEBUGOUT_Event(String msg,int[] value=null);
        public delegate void eBus_boot_ReadMem_Event(List<byte> btl);

        private eBus_boot_PACKSEND_Event pack_send=null;
        private eBus_boot_DEBUGOUT_Event debug_out = null;
        private eBus_boot_ReadMem_Event read_mem_event_ = null;

        /// <summary>
        /// boot状态枚举
        /// </summary>
        public enum eBus_boot_State_enum
        {
            eBus_boot_State_free = 0,
            eBus_boot_State_in_boot,
            eBus_boot_State_request,
            eBus_boot_State_write,
            eBus_boot_State_read,
            eBus_boot_State_write_finish,
            eBus_boot_State_read_finish,
            eBus_boot_State_err,
        };



        

        /// <summary>
        /// boot当前状态
        /// </summary>
        public eBus_boot_State_enum eBus_boot_now_state = eBus_boot_State_enum.eBus_boot_State_free;
        /// <summary>
        /// boot目标状态
        /// </summary>
        private eBus_boot_State_enum eBus_boot_obj_state = eBus_boot_State_enum.eBus_boot_State_free;


        /// <summary>
        /// 读写数据超时，时间记录
        /// </summary>
        private int eBus_boot_rw_time_out = 0;
        private Thread eBus_boot_readwrite_mem_th;


        #region 内存写相关变量

            /// <summary>
            /// app bin文件字节流
            /// </summary>
            private byte[] write_mem_data_buff;
            /// <summary>
            /// 当前读写过程的偏移地址
            /// </summary>
            private int write_mem_offset = 0;
            /// <summary>
            /// 默认单次读写长度字节数
            /// </summary>
            private int write_mem_packdata_len_def = 512;

            private int write_mem_pagedata_len = 0;

        #endregion

 


        #region 读内存相关变量
        /// <summary>
        /// 读内存地址偏移
        /// </summary>
        private int read_mem_offset = 0;
        /// <summary>
        /// 读内存包数据缺省长度
        /// </summary>
        private int read_mem_packdata_len_def = 512;
        /// <summary>
        /// 读内存包数据实际长度
        /// </summary>
        private int read_mem_pagedata_len;
        /// <summary>
        /// 读内存总长度
        /// </summary>
        private int read_mem_full_length;

        private byte[] read_mem_data_buff;

        #endregion




        public eBus_boot(eBus_boot_PACKSEND_Event pack_send,eBus_boot_DEBUGOUT_Event debugout=null,eBus_boot_ReadMem_Event read_mem_event=null)
        {
            this.pack_send = pack_send;
            this.debug_out = debugout;
            this.read_mem_event_ = read_mem_event;
        }
        private void eBus_boot_debug_out(String msg,int[] value=null)
        {
            if (debug_out != null && msg != null && msg.Length > 0)
            { 
                debug_out(msg,value);
            } 
        }
        public void eBus_boot_reset()
        {
            eBus_boot_pack_send(eBus_boot_creat_cmd_pack(EBUS_MCUBOOT_RESET, null));
        }

        public void eBus_boot_write_mem_start(byte[] file_value,int packlen)
        {
            if (file_value == null || file_value.Length == 0)
            {
                eBus_boot_now_state = eBus_boot_State_enum.eBus_boot_State_err;
                return;
            } 

            write_mem_data_buff = new Byte[file_value.Length];
            file_value.CopyTo(write_mem_data_buff, 0);

            eBus_boot_obj_state = eBus_boot_State_enum.eBus_boot_State_write;
            eBus_boot_now_state = eBus_boot_State_enum.eBus_boot_State_in_boot;

            write_mem_offset = 0;
            write_mem_packdata_len_def = packlen;

            eBus_boot_readwrite_mem_th = new Thread(eBus_boot_run);
            eBus_boot_readwrite_mem_th.IsBackground = true;
            eBus_boot_readwrite_mem_th.Start();
        }

        public void eBus_boot_read_mem_start(int offset,int packdata_len,int full_len)
        {
            read_mem_data_buff = new byte[full_len];
            eBus_boot_obj_state = eBus_boot_State_enum.eBus_boot_State_read ;
            eBus_boot_now_state = eBus_boot_State_enum.eBus_boot_State_in_boot;

            read_mem_offset = offset;
            read_mem_packdata_len_def = packdata_len;
            read_mem_full_length = full_len;

            eBus_boot_readwrite_mem_th = new Thread(eBus_boot_run);
            eBus_boot_readwrite_mem_th.IsBackground = true;
            eBus_boot_readwrite_mem_th.Start();
        }

        public eBus_pack_struct eBus_boot_creat_cmd_pack(Byte cmd,List<byte> value)
        {
            eBus_pack_struct pack = new eBus_pack_struct() ;
            pack.head1 = 0xfe;
            pack.head2 = 0xef;
            pack.ptype = 0xF0;
            pack.data = new List<byte>();
            pack.data.Add(cmd);
            if(value!=null&&value.Count>0)
            {
                pack.data.AddRange(value);
            }
            return pack;
        }

        private Boolean eBus_boot_pack_send(eBus_pack_struct pack)
        {
            if (pack_send != null&&pack!=null)
            { 
                pack_send(pack);
                return true;
            } 
            return false;
        }

        private int eBus_boot_pack_send_write_mem(int offset, int max_len, int all_size)
        {
            if (offset >= all_size) return 0;
            if (all_size - offset < max_len)
            {
                max_len = all_size - offset;
            }
            List<byte> btl = new List<byte>();
            btl.AddRange(Function.Uint32_2_bytes((UInt32)offset).ToArray());
            btl.AddRange(Function.Uint32_2_bytes((UInt32)max_len).ToArray());
            for (int i = 0; i < max_len; i++)
            {
                btl.Add(write_mem_data_buff[offset + i]);
            }

            eBus_boot_pack_send(eBus_boot_creat_cmd_pack(EBUS_MCUBOOT_DATA_WRITE, btl));

            eBus_boot_debug_out("eBus_boot_write_mem:request offset:" + write_mem_offset.ToString() + ",length:" + write_mem_pagedata_len.ToString() + "\r\n");

            return max_len;
        }

        private int eBus_boot_pack_send_read_mem(int offset,int max_len,int all_size)
        {
            if (offset >= all_size)
            {
                throw new Exception("offset参数必须小于all_size");
            }
            if (max_len < 1)
            {
                throw new Exception("max_len参数必须大于1");
            }
            if (all_size - offset < max_len)
            {
                max_len = all_size - offset;
            }
            List<byte> btl = new List<byte>();
            btl.AddRange(Function.Uint32_2_bytes((UInt32)offset));
            btl.AddRange(Function.Uint32_2_bytes((UInt32)max_len));
            eBus_boot_pack_send(eBus_boot_creat_cmd_pack(EBUS_MCUBOOT_DATA_READ, btl));

            eBus_boot_debug_out("eBus_boot_read_mem:request offset:" + offset.ToString() + ",length:" + max_len.ToString() + "\r\n");
            return max_len;
        }

        public void eBus_boot_receive(eBus_pack_struct pack)
        {
            if (pack.ptype != 0xF0) return;
            if (pack.data == null) return;
            if (pack.data.Count < 1) return;
            //
            if (pack.data.Count == 1)
            {
                if (pack.data[0] == EBUS_MCUBOOT_RESET_ACK)
                {
                    eBus_boot_debug_out("eBus_boot_reset:ack\r\n");
                }
            }
            switch (eBus_boot_now_state)
            {
                case eBus_boot_State_enum.eBus_boot_State_in_boot:
                    if (pack.data.Count != 1) break;
                    if (pack.data[0] == EBUS_MCUBOOT_ENABLE_ACK)
                    {
                        eBus_boot_debug_out("in_boot:成功进入boot\r\n");
                        if (eBus_boot_obj_state == eBus_boot_State_enum.eBus_boot_State_write)
                        {
                            eBus_boot_now_state = eBus_boot_State_enum.eBus_boot_State_request;
                            //
                            eBus_boot_debug_out("发送请求升级命令，等待响应...\r\n");
                            eBus_boot_pack_send(eBus_boot_creat_cmd_pack(EBUS_MCUBOOT_REQUEST, Function.Uint32_2_bytes((UInt32)write_mem_data_buff.Length)));
                        }
                        else if (eBus_boot_obj_state == eBus_boot_State_enum.eBus_boot_State_read)
                        {
                            eBus_boot_now_state = eBus_boot_State_enum.eBus_boot_State_read;
                            read_mem_pagedata_len=eBus_boot_pack_send_read_mem(read_mem_offset, read_mem_packdata_len_def, read_mem_full_length);
                        }

                    }
                    break;
                
                //判断请求
                case eBus_boot_State_enum.eBus_boot_State_request: 
                    if (pack.data.Count != 2) break;
                    if (pack.data[0] == EBUS_MCUBOOT_REQUEST_ACK)
                    {
                        switch (pack.data[1])
                        {
                            case 0:
                                eBus_boot_debug_out("允许升级，开始写入数据...\r\n");

                                eBus_boot_now_state = eBus_boot_State_enum.eBus_boot_State_write;
                                write_mem_pagedata_len = eBus_boot_pack_send_write_mem(write_mem_offset,write_mem_packdata_len_def,write_mem_data_buff.Length);
                                
                                break;
                            default: 
                                eBus_boot_debug_out("请求升级被拒绝，错误代码：" + pack.data[1].ToString()+"\r\n"); 
                                break;
                        } 
                    }
                    break;

                case eBus_boot_State_enum.eBus_boot_State_write:
                    if (pack.data.Count != 10) break;
                    if (pack.data[0] == EBUS_MCUBOOT_DATA_WRITE_ACK)
                    {
                        int w_offet_ = pack.data[1] + pack.data[2] * 0x100 + pack.data[3] * 0x10000 + pack.data[4] * 0x1000000;
                        int w_len_ = pack.data[5] + pack.data[6] *0x100 + pack.data[7] *0x10000 + pack.data[8] *0x1000000;
                        //
                        switch (pack.data[9])
                        {
                            case 0:
                                if (w_offet_ != write_mem_offset)
                                {
                                    eBus_boot_debug_out("eBus_boot_write_mem:err write_mem_offset\r\n");
                                    break;
                                }
                                if (write_mem_pagedata_len != w_len_)
                                {
                                    eBus_boot_debug_out("eBus_boot_write_mem:err write_mem_pagedata_len\r\n");
                                    break;
                                }
                                eBus_boot_debug_out("eBus_boot_write_mem:ack offset:" + w_offet_.ToString() + ",length:" + w_len_.ToString() + "\r\n", new int[] { w_offet_, w_len_ });
                                if (w_offet_ + w_len_ == write_mem_data_buff.Length)
                                {
                                    eBus_boot_now_state = eBus_boot_State_enum.eBus_boot_State_write_finish;
                                    eBus_boot_debug_out("eBus_boot_write_mem:finish fulllength:" + write_mem_data_buff.Length.ToString() + "\r\n");
                                    break;
                                }
                                write_mem_offset += write_mem_pagedata_len;
                                write_mem_pagedata_len = eBus_boot_pack_send_write_mem(write_mem_offset, write_mem_packdata_len_def, write_mem_data_buff.Length);
                                break;
                            default: eBus_boot_debug_out("eBus_boot_write_mem:err number:" + pack.data[9].ToString() + "\r\n"); break;
                        } 
                    }
                    break;
                case eBus_boot_State_enum.eBus_boot_State_read:
                    if (pack.data.Count < 10) break;
                    if (pack.data[0] == EBUS_MCUBOOT_DATA_READ_ACK)
                    {
                        int r_offet_ = pack.data[1] + pack.data[2] * 0x100 + pack.data[3] * 0x10000 + pack.data[4] * 0x1000000;
                        int r_len_ = pack.data[5] + pack.data[6] * 0x100 + pack.data[7] * 0x10000 + pack.data[8] * 0x1000000;
                        switch (pack.data[9])
                        {
                            case 0:
                                if (r_offet_ != read_mem_offset)
                                {
                                    eBus_boot_debug_out("eBus_boot_read_mem:err read_mem_offset\r\n");
                                    break;
                                }
                                if (r_len_ != read_mem_pagedata_len)
                                {
                                    eBus_boot_debug_out("eBus_boot_read_mem:err read_mem_pagedata_len\r\n");
                                    break;
                                }
                                pack.data.CopyTo(10, read_mem_data_buff, r_offet_, r_len_);
                                eBus_boot_debug_out("eBus_boot_read_mem:ack offset:" + r_offet_.ToString() + ",length:" + r_len_.ToString() + "\r\n", new int[] { r_offet_, r_len_ });
                                if (r_offet_ + r_len_ >= read_mem_full_length)
                                {
                                    eBus_boot_now_state = eBus_boot_State_enum.eBus_boot_State_read_finish;
                                    eBus_boot_debug_out("eBus_boot_read_mem:finish fulllength:" + read_mem_data_buff.Length.ToString()  + "\r\n");
                                    if (this.read_mem_event_ != null) this.read_mem_event_(read_mem_data_buff.ToList());
                                    break;
                                }
                                read_mem_offset += r_len_;
                                read_mem_pagedata_len = eBus_boot_pack_send_read_mem(read_mem_offset, read_mem_packdata_len_def, read_mem_full_length);

                                break;

                            default:
                                eBus_boot_debug_out("eBus_boot_read_mem:err number:" + pack.data[9].ToString() + "\r\n", new int[] { pack.data[9] });
                                break;
                        }
                    }
                    break;

                default: eBus_boot_now_state = 0; break;
            }
        }
        private void eBus_boot_run()
        {
            while (eBus_boot_now_state != eBus_boot_State_enum.eBus_boot_State_free && 
                    eBus_boot_now_state != eBus_boot_State_enum.eBus_boot_State_write_finish&&
                    eBus_boot_now_state != eBus_boot_State_enum.eBus_boot_State_read_finish)
            {
                switch (eBus_boot_now_state)
                {
                    case eBus_boot_State_enum.eBus_boot_State_in_boot: 
                        eBus_boot_pack_send(eBus_boot_creat_cmd_pack(EBUS_MCUBOOT_ENABLE,null));
                        break;
                    default: break;
                }
                Thread.Sleep(10);
            }
        }



    }










}
